package com.openatc.agent.service.impl;

import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import com.openatc.agent.service.AscsDao;
import com.openatc.agent.service.DevService;
import com.openatc.agent.service.MessageService;
import com.openatc.agent.utils.TokenUtil;
import com.openatc.comm.data.MessageData;
import com.openatc.core.model.RESTRet;
import com.openatc.core.util.DateUtil;
import com.openatc.model.model.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
import java.util.stream.Collectors;

import static com.openatc.comm.common.CosntDataDefine.*;
import static com.openatc.comm.common.CosntDataDefine.ControlPattern;
import static com.openatc.core.util.DateUtil.compareCreateTime;

/**
 * @Classname DevSerivceImpl
 * @Description
 * @Date 2022/4/8 18:41
 * @Created by panleilei
 */
@Service
public class DevSerivceImpl implements DevService {

    @Autowired
    private AscsDao mDao;

    private Gson gson = new Gson();
    @Autowired
    private CommServiceDown commServiceDown;
    @Autowired
    private MessageService messageService;

    private final Logger logger = Logger.getLogger(DevSerivceImpl.class.getName());

    private static Map<String,Map<Integer,Integer>> detectorDataMap = new ConcurrentHashMap<>();    // 记录每个路口每个检测器的默认饱和流量值

    private static Map<String,Map<Integer,Long>> detectorUpdateRecord = new ConcurrentHashMap<>(); // 记录检测器的饱和流量值的更新状态

    /**
     * 根据用户角色返回设备
     * @return
     */
    @Override
    public List<AscsBaseModel> getDevs() {
//        String whereCondition = "";
//        String code = mDao.getSqlByUserRole();
//        if (code != null){
//            String temp = code;
//            whereCondition = mDao.addWhereCondition(whereCondition,temp);
//        }
//
//        String sql = "SELECT id, thirdplatformid, platform, gbid, firm, agentid, protocol, geometry, code,mode,control, type, status, descs, name,jsonparam, case (LOCALTIMESTAMP - lastTime)< '5 min' when 'true' then 'UP' else 'DOWN' END AS state,lastTime,sockettype,tags,event_type FROM dev";
//        sql = sql.concat(whereCondition);
//        sql = sql.concat(" ORDER BY agentid");
//        List<AscsBaseModel> devs = mDao.getDevByPara(sql);
//        return RESTRetUtils.successObj(devs);


        String userName = TokenUtil.getCurrentUserName();
        return getDevsByUserRoleAndOrg(userName);
    }

    @Override
    public List<AscsBaseModel> getDevsByUserName(String userName) {
        return getDevsByUserRoleAndOrg(userName);
    }

    @Override
    public List<Phase> getPhases(String agentid) {
        MessageData phaseRequest = new MessageData();
        phaseRequest.setInfotype(phase);
        phaseRequest.setOperation(getrequest);
        phaseRequest.setAgentid(agentid);
        RESTRet responceData = messageService.postDevsMessage(phaseRequest);

        if (responceData.isSuccess() && responceData.getData() != null) {
            MessageData data = (MessageData)responceData.getData();
            List<Phase> phases = gson.fromJson(data.getData().getAsJsonObject().get("phaseList"), new TypeToken<List<Phase>>() {
            }.getType());
            return phases;
        } else {
            return null;
        }
    }

    @Override
    public List<Phase> getPhases(String agentid, List<Integer> directions) {
        // 构建相位请求数据结构
        MessageData phaseRequest = new MessageData();
        phaseRequest.setInfotype(phase);
        phaseRequest.setOperation(getrequest);
        phaseRequest.setAgentid(agentid);
        RESTRet responceData = messageService.postDevsMessage(phaseRequest);

        if (responceData.isSuccess() && responceData.getData() != null) {
            List<Phase> resPhase = new ArrayList<>();
            MessageData data = (MessageData)responceData.getData();
            List<Phase> phases = gson.fromJson(data.getData().getAsJsonObject().get("phaseList"), new TypeToken<List<Phase>>() {
            }.getType());
            for (Phase phase1 : phases) {
                if (!Collections.disjoint(directions, phase1.getDirection())) {
                    resPhase.add(phase1);
                }
            }
            return resPhase;
        } else {
            return null;
        }

    }

    @Override
    public MessageData createPhaseLockMsg(String agentid, List<Phase> phaseList, JsonObject phaseLockInfo) {
        MessageData messageData = new MessageData();

        messageData.setOperation(setrequest);
        messageData.setInfotype(ControlPattern);
        messageData.setAgentid(agentid);

        JsonArray phases = new JsonArray();
        for (Phase phase : phaseList) {
            JsonObject phaseObj = new JsonObject();

            phaseObj.addProperty("id",phase.getId());
            phaseObj.addProperty("type",phaseLockInfo.get("type").getAsInt());

            phases.add(phaseObj);
        }
        phaseLockInfo.add("phases",phases);
        phaseLockInfo.remove("type");

        JsonObject data = new JsonObject();
        data.addProperty("control",22);
        data.add("data",phaseLockInfo);

        messageData.setData(data);

        return messageData;
    }

    /**
     * 根据用户的角色和组织机构获取名下的设备
     * @param userName
     * @return
     */
    private List<AscsBaseModel> getDevsByUserRoleAndOrg(String userName) {
        // 1、管理员 应答当前所有设备
        // 2、非管理员 应答所在组织机构的设备
        String basesql = String.format("SELECT id, thirdplatformid, platform, gbid, firm, agentid, protocol, geometry, code,mode,control, type, status, descs, name,jsonparam, case (LOCALTIMESTAMP - lastTime)< '2 min' when 'true' then 'UP' else 'DOWN' END AS state,lastTime,sockettype,tags FROM dev ");
        String caseSql = String.format(" where (CASE WHEN ( SELECT userrole FROM (SELECT r.name userrole FROM t_role r JOIN t_user_role ur ON r.id = ur.role_id " +
                        "JOIN t_user u ON ur.user_id = u.id  WHERE u.user_name = '%s') as usr1 WHERE position('admin' in usr1.userrole) > 0) NOTNULL THEN 1 = 1" +
                        "WHEN (SELECT userrole FROM (SELECT r.name userrole FROM t_role r JOIN t_user_role ur  ON r.id = ur.role_id JOIN t_user u " +
                        "ON ur.user_id = u.id WHERE u.user_name = '%s') as usr1 WHERE position('superadmin' in usr1.userrole) > 0) NOTNULL THEN 1 = 1 " +
                        " WHEN (SELECT count(organization) FROM t_user WHERE user_name = '%s') = 0 " +
                        " or (SELECT organization FROM t_user WHERE user_name = '%s') = '' THEN code IS NULL " +
                        " WHEN (SELECT organization FROM t_user WHERE user_name = '%s') = '*' " +
                        " THEN 1 = 1 " +
                        " WHEN (SELECT count(organization) FROM t_user WHERE user_name = '%s') = 1 " +
                        " THEN code  ILIKE regexp_replace(concat('%%',(SELECT organization FROM t_user WHERE user_name = '%s'),'%%'),'\\\\','\\\\\\','g') " +
//                        " THEN code ~ (SELECT organization FROM t_user WHERE user_name = '%s')" +
                        " ELSE 1 = 2 END)",
                userName,userName,userName,userName,userName,userName,userName);

        String sql = "";
        if (userName != null) {
            sql = basesql.concat(caseSql);
        } else {
            sql = basesql;
        }
        return mDao.getDevByPara(sql);
    }

    /**
     * 获取路口检测器状态
     * @param agentid
     * @return
     */
    @Override
    public StatusDetector getDetectorStatusByAgentid(String agentid) {
        String key = "asc:status/detector:" + agentid;
        String value = mDao.getValueFromRedis(key);
        if (value == null) return new StatusDetector();

        // 初始化对象
        StatusDetector statusDetector = new StatusDetector();
        statusDetector.setDetector(new ArrayList<>());
        statusDetector.setIo(new ArrayList<>());

//        if (value == null) {
//            return statusDetector;
//        }

        MessageData messageData;
        try {
            messageData = gson.fromJson(value, MessageData.class);
            statusDetector = gson.fromJson(messageData.getData(), StatusDetector.class);
        } catch (JsonSyntaxException e){
            logger.warning("Json parse Exception: " + e.getMessage());
            return statusDetector;
        }

        // 车辆检测器
        List<Detector> detector = statusDetector.getDetector();
        List<Detector> effectiveDetector = getEffectiveDetector(detector);
        statusDetector.setDetector(effectiveDetector);

        // io版
        List<Detector> ioDetector = statusDetector.getIo();
        List<Detector> effectiveIoDetector = getEffectiveDetector(ioDetector);
        statusDetector.setIo(effectiveIoDetector);

        return statusDetector;
    }

    /**
     * 获取相位统计数据（实时）
     * @param agentid
     * @param phaseno
     * @return
     */
    @Override
    public PhasesStatisticsVo getPhasesStatistics(String agentid, int phaseno) {
        String key = "asc:statistics/phase:" + agentid;
        String value = mDao.getValueFromRedis(key);

        PhasesStatisticsVo phasesStatisticsVo;
        try {
            phasesStatisticsVo = gson.fromJson(value, PhasesStatisticsVo.class);
            phasesStatisticsVo.setCreateTime(null);
        } catch (JsonSyntaxException e){
            logger.warning("Json parse Exception: " + e.getMessage());
            return new PhasesStatisticsVo();
        }
        // 返回指定相位的实时数据
        if (phaseno != -1) {
            List<List<PhaseStatistics>> phasesstatisticsList = phasesStatisticsVo.getPhasesstatisticsList();
            if (!CollectionUtils.isEmpty(phasesstatisticsList)) {
                List<PhaseStatistics> phaseStatistics = phasesstatisticsList.get(0);
                List<PhaseStatistics> phaseStatisticsList = new ArrayList<>();
                for (PhaseStatistics data : phaseStatistics) {
                    if (data.getPhaseno() == phaseno){
                        phaseStatisticsList.add(data);
                        break;
                    }
                }
                phasesstatisticsList.add(phaseStatisticsList);
                phasesstatisticsList.clear();
                phasesstatisticsList.add(phaseStatisticsList);
                phasesStatisticsVo.setPhasesstatisticsList(phasesstatisticsList);
            }
        }
        return phasesStatisticsVo;
    }

    /**
     * 获取历史相位统计数据
     * @param agentid
     * @param jsonObject
     * @return
     */
    @Override
    public PhasesStatisticsVo getHistoryPhasesStatistics(String agentid, JsonObject jsonObject) {
        String dataLabel = "phasesStatistics";
        String beginTime = jsonObject.get("beginTime").getAsString();
        String endTime = jsonObject.get("endTime").getAsString();
        int phaseno = jsonObject.get("phaseno").getAsInt();

        List<String> values = mDao.getStatistics(agentid,dataLabel,beginTime,endTime);

        PhasesStatisticsVo vo = new PhasesStatisticsVo();
        vo.setCreateTime(null);
        if (CollectionUtils.isEmpty(values)) {
            return vo;
        }

        List<List<PhaseStatistics>> psList = new ArrayList<>();

        for (String value : values) {
            PhasesStatisticsVo phasesStatisticsVo;
            try {
                phasesStatisticsVo = gson.fromJson(value, PhasesStatisticsVo.class);
            } catch (JsonSyntaxException e) {
                logger.warning("Json pares Exception: " + e.getMessage());
                continue;
            }

            if (phasesStatisticsVo == null || CollectionUtils.isEmpty(phasesStatisticsVo.getPhasesstatisticsList())) {
                continue;
            }

            List<List<PhaseStatistics>> phasesstatisticsList = phasesStatisticsVo.getPhasesstatisticsList();
            List<PhaseStatistics> list = phasesstatisticsList.get(0);

            // 指定了相位号
            if (phaseno != -1) {
                List<PhaseStatistics> tempList = new ArrayList<>();
                for (PhaseStatistics phaseStatistics : list) {
                    if (phaseStatistics.getPhaseno() == phaseno){
                        tempList.add(phaseStatistics);
                        psList.add(tempList);
                    }
                }
            } else {
                psList.add(list);
            }
        }

        vo.setPhasesstatisticsList(psList);

        return vo;
    }

    /**
     * 保存相位和路口统计数据
     * @param jsonObject
     * @param msg
     */
    @Override
    public void savePhasesAndInteStatistics(JsonObject jsonObject, MessageData msg) {

        JsonObject feature = jsonObject.get("param").getAsJsonObject();
        JsonArray phaseArray = feature.get("phaseList").getAsJsonArray();
        JsonArray detectorArray = feature.get("detectorList").getAsJsonArray();
        String agentid = msg.getAgentid();

        JsonElement data = msg.getData();
        Volume volume = null;
        try {
            volume = gson.fromJson(data, Volume.class);
        } catch (JsonSyntaxException e){
            logger.warning("Json parse Exception: " + e.getMessage());
        }

        // 计算检测器的所有数据
        Map<Integer,Map<String,Integer>> volumeMap = getAllDataOfDetector(volume);

        // 计算相位统计数据
        List<PhaseStatistics> phasesStatistics = computePhasesStatistics(phaseArray,detectorArray, agentid,volumeMap);

        // 保存相位统计数据
        savePhasesStatistics(phasesStatistics,agentid);

        // 计算路口统计数据
        IntersectionStatistics intesData = computeIntesStatistics(phasesStatistics, volume,agentid,volumeMap);

        // 保存路口统计数据
        saveIntesStatistics(intesData,agentid);

        // 修正当前路口检测器的饱和流量默认值
        modifySaturation(agentid,volumeMap);

    }

    /**
     * 计算检测器所有的数据
     * @param volume
     * @return
     */
    private Map<Integer, Map<String, Integer>> getAllDataOfDetector(Volume volume) {
        if (volume == null || volume.getDetector() == null) {
            return null;
        }
        String flow = "flow";
        String occupyrate = "occupyrate";
        String greenusage = "greenusage";

        Map<Integer,Map<String,Integer>> map = new HashMap<>();
        List<VolumeDetector> detectors = volume.getDetector();
        for (VolumeDetector detector : detectors) {
            Map<String,Integer> dataMap = new HashMap<>();
            int totalflow = detector.getSmallvehnum() + detector.getMiddlevehnum() + detector.getLargevehnum();
            dataMap.put(flow,totalflow);
            dataMap.put(occupyrate,detector.getOccupyrate());
            dataMap.put(greenusage,detector.getGreenusage());

            map.put(detector.getId(),dataMap);
        }
        return map;
    }

    /**
     * 修正当前路口检测器的饱和流量默认值（减小）
     * 理论上不会出现多个线程同时操作一个agentid对应的map，此处先不加 'synchronized' todo
     * @param agentid
     * @param volumeMap
     */
    private void modifySaturation(String agentid, Map<Integer, Map<String, Integer>> volumeMap) {
        // 增大检测器默认的流量饱和值
        addDetectorDefaultValue(agentid,volumeMap);
        // 减小检测器默认的流量饱和值
        subDetectorDefaultValue(agentid,volumeMap);
    }

    /**
     * 减小检测器的默认流量饱和值
     * @param agentid
     * @param volumeMap
     */
    private void subDetectorDefaultValue(String agentid, Map<Integer, Map<String, Integer>> volumeMap) {
        // 如果7天内，该检测器都没有达到饱和值，说明检测器饱和值设置不合理，需要减小
        int subValue = 1000 * 60 * 60 * 24 * 7;
        Map<Integer, Integer> dataMap = detectorDataMap.get(agentid);
        if (dataMap == null) return;

        Map<Integer, Long> recordMap = detectorUpdateRecord.get(agentid);
        if (recordMap == null) {
            recordMap = new ConcurrentHashMap<>();
            detectorUpdateRecord.put(agentid,recordMap);
        }

        for (Integer key : dataMap.keySet()) {
            Long lastModifyTime = recordMap.get(key);
            long now = System.currentTimeMillis();
            if (lastModifyTime == null) {
                lastModifyTime = now;
                recordMap.put(key,lastModifyTime);
                continue;
            }
            if (now - lastModifyTime > subValue) {
                int subModifyValue = getSubModifyValue(dataMap.get(key));
                dataMap.put(key,subModifyValue);
                recordMap.put(key,now);
            }
        }

    }

    /**
     * 增加检测器的默认流量饱和值
     * @param agentid
     * @param volumeMap
     */
    private void addDetectorDefaultValue(String agentid, Map<Integer, Map<String, Integer>> volumeMap) {
        // 如果上报的检测器的流量超过了默认的饱和值，扩充默认流量饱和值
        Map<Integer, Integer> map = detectorDataMap.get(agentid);
        if (map == null) {
            map = new HashMap<>();
            detectorDataMap.put(agentid,map);
        }

        for (Integer key : volumeMap.keySet()) {
            // 当前检测器的流量大于默认的流量，更新默认值
            int defaultValue = map.get(key) == null ? 0 : map.get(key);
            if (volumeMap.get(key).get("flow") > defaultValue) {
                Integer newValue = getModifyValue(defaultValue,volumeMap.get(key).get("flow"));
                map.put(key,newValue);
                updateDetectorModifyStatus(agentid,key);
            }
        }
    }

    /**
     * 计算检测器饱和流量值减小的值
     * @param i
     * @return
     */
    private int getSubModifyValue(Integer i) {
        // 减小为0
        return 0;
    }

    /**
     * 保存相位统计数据
     * @param phasesStatistics
     * @param agentid
     */
    private void savePhasesStatistics(List<PhaseStatistics> phasesStatistics, String agentid) {
        PhasesStatisticsVo vo = initStatisticsStructure(phasesStatistics, agentid);
        // 保存相位统计数据到redis
        saveStatisticsToRedis(vo,agentid);
        // 保存相位统计数据到数据库
        savaStatisticsToDB(vo,agentid);
    }

    /**
     * 初始化相位数据结构
     * @param phasesStatistics
     * @param agentid
     * @return
     */
    private PhasesStatisticsVo initStatisticsStructure(List<PhaseStatistics> phasesStatistics, String agentid) {
        // 初始化数据结构
        PhasesStatisticsVo phasesStatisticsVo = new PhasesStatisticsVo();
        phasesStatisticsVo.setCreateTime(DateUtil.date2str(new Date()));
        phasesStatisticsVo.setAgentid(agentid);

        List<List<PhaseStatistics>> psList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(phasesStatistics)) {
            psList.add(phasesStatistics);
        }
        phasesStatisticsVo.setPhasesstatisticsList(psList);
        return phasesStatisticsVo;
    }

    /**
     * 保存路口统计数据
     * @param intesStatisticsData
     * @param agentid
     */
    private void saveIntesStatistics(IntersectionStatistics intesStatisticsData, String agentid) {
        // 初始化数据结构
        IntersectionStatisticsVo intersectionStatisticsVo = initStatisticsStructure(intesStatisticsData,agentid);
        // 保存路口统计数据到redis
        saveStatisticsToRedis(intersectionStatisticsVo, agentid);
        // 保存路口统计数据到数据库
        savaStatisticsToDB(intersectionStatisticsVo, agentid);
    }

    /**
     * 保存路口统计数据到数据库
     * @param intesStatisticsDataVo
     * @param agentid
     */
    private void savaStatisticsToDB(IntersectionStatisticsVo intesStatisticsDataVo, String agentid) {
        String data = gson.toJson(intesStatisticsDataVo);
        String dataLabel = "intersectionStatistics";
        mDao.saveStatistics(data,agentid,dataLabel);
    }

    /**
     * 初始化路口统计数据结构
     * @param intesStatisticsData
     * @param agentid
     * @return
     */
    private IntersectionStatisticsVo initStatisticsStructure(IntersectionStatistics intesStatisticsData, String agentid) {
        IntersectionStatisticsVo intesStatisticsVo = new IntersectionStatisticsVo();

        List<IntersectionStatistics> list = new ArrayList<>();
        if (intesStatisticsData != null){
            list.add(intesStatisticsData);
        }

        intesStatisticsVo.setIntestatistics(list);
        intesStatisticsVo.setCreateTime(DateUtil.date2str(new Date()));
        intesStatisticsVo.setAgentid(agentid);
        return intesStatisticsVo;
    }

    /**
     * 保存相位统计数据到数据库
     * @param phasesStatisticsVo
     * @param agentid
     */
    private void savaStatisticsToDB(PhasesStatisticsVo phasesStatisticsVo, String agentid) {
        // 保存相位统计数据到数据库
        String dataLabel = "phasesStatistics";
        mDao.saveStatistics(gson.toJson(phasesStatisticsVo),agentid,dataLabel);
    }

    /**
     * 保存路口统计数据到redis
     * @param intesStatisticsDataVo
     * @param agentid
     */
    private void saveStatisticsToRedis(IntersectionStatisticsVo intesStatisticsDataVo, String agentid) {

        String key = "asc:statistics/intersection:" + agentid;
        String value = gson.toJson(intesStatisticsDataVo);

        mDao.saveValueToRedis(key,value);
        mDao.publishMsg(key,value);
    }

    /**
     * 保存相位统计数据到redis
     * @param phasesStatisticsVo
     * @param agentid
     */
    private void saveStatisticsToRedis(PhasesStatisticsVo phasesStatisticsVo, String agentid) {


        // 保存数据
        String key = "asc:statistics/phase:" + agentid;
        String value = gson.toJson(phasesStatisticsVo);
        mDao.saveValueToRedis(key,value);
        mDao.publishMsg(key,value);
    }

    /**
     * 计算路口统计数据
     * @param phasesStatistics
     * @param volume
     * @param agentid
     * @param volumeMap
     * @return
     */
    private IntersectionStatistics computeIntesStatistics(List<PhaseStatistics> phasesStatistics, Volume volume, String agentid, Map<Integer, Map<String, Integer>> volumeMap) {
        if (volume == null) return null;

        List<VolumeDetector> detectors = volume.getDetector();
        if (CollectionUtils.isEmpty(detectors)) return null;
        // 没有相位统计数据
        if (CollectionUtils.isEmpty(phasesStatistics)) return null;

        IntersectionStatistics intesStatisticsData = new IntersectionStatistics();
        // 路口总流量
        int totalFlow = getIntersectionTotalFlow(detectors);
        // 路口平均流量
//        int averageFlow = totalFlow / detectors.size();
        int averageFlow = totalFlow / phasesStatistics.size();
        // 路口平均流量饱和度
//        int averageSaturation = getIntersectionAvgSaturation(detectors, agentid,volumeMap);
        int averageSaturation = getIntersectionAvgSaturation(phasesStatistics);
        // 占有率
        int occupyrate = getIntersectionOccupyrate(detectors);
        // 路口拥堵指数
        String congestionIndex = getIntersectionCongestionIndex(phasesStatistics);
        // 路口失衡指数
        String imbalanceIndex = getIntersectionImbalanceIndex(phasesStatistics);
        // 路口绿灯占有率
        int greenusage = getIntersectionGreenusage(phasesStatistics);
        String time = DateUtil.date2str(new Date());

        intesStatisticsData.setTotalflow(totalFlow);
        intesStatisticsData.setAverageflow(averageFlow);
        intesStatisticsData.setSaturation(averageSaturation);
        intesStatisticsData.setOccupyrate(occupyrate);
        intesStatisticsData.setCongestionindex(congestionIndex);
        intesStatisticsData.setImbalanceindex(imbalanceIndex);
        intesStatisticsData.setGreenusage(greenusage);
        intesStatisticsData.setTime(time);
        return intesStatisticsData;
    }

    /**
     * 路口平均流量饱和度
     * @param phasesStatistics
     * @return
     */
    private int getIntersectionAvgSaturation(List<PhaseStatistics> phasesStatistics) {
        int total = 0;
        for (PhaseStatistics phaseStatistics : phasesStatistics) {
            PhaseStatisticsData phasestatistics = phaseStatistics.getPhasestatistics();
            Integer saturation = phasestatistics.getSaturation();
            if (saturation == null) continue;
            total += saturation;
        }
        return divide(total,phasesStatistics.size());
    }

    /**
     * 路口绿灯占有率
     * @param phasesStatistics
     * @return
     */
    private int getIntersectionGreenusage(List<PhaseStatistics> phasesStatistics) {
        int total = 0;
        for (PhaseStatistics phaseStatistics : phasesStatistics) {
            Integer value = phaseStatistics.getPhasestatistics().getGreenusage();
            if (value == null) continue;
            total += phaseStatistics.getPhasestatistics().getGreenusage();
        }
        return divide(total,phasesStatistics.size());
    }

    /**
     * 路口失衡指数（相位占有率差值）
     * @param phasesStatistics
     * @return
     */
    private String getIntersectionImbalanceIndex(List<PhaseStatistics> phasesStatistics) {
        int max = 0;
        int min = 100; // 占有率最高100
        for (PhaseStatistics phaseStatistics : phasesStatistics) {
            Integer temp = phaseStatistics.getPhasestatistics().getOccupyrate();
            if (temp == null) continue;
            if (temp > max) {
                max = temp;
            }
            if (temp < min) {
                min = temp;
            }
        }
        String imbalanceIndex = computeIntersectionImbalanceIndex(max,min);
        return imbalanceIndex;
    }

    /**
     * 转换路口失衡指数
     * @param max
     * @param min
     * @return
     */
    private String computeIntersectionImbalanceIndex(int max, int min) {
        int subValue = Math.abs(max - min);

        if (subValue >= 0 &&  subValue < 10) {
            return "A";
        }
        if (subValue >= 10 && subValue < 20) {
            return "B";
        }
        if (subValue >= 20 && subValue < 30) {
            return "C";
        }
        if (subValue >= 30 && subValue < 40) {
            return "D";
        }
        return "E";
    }

    /**
     * 路口拥堵指数
     * @param phasesStatistics
     * @return
     */
    private String getIntersectionCongestionIndex(List<PhaseStatistics> phasesStatistics) {
        // 取相位拥堵指数最高
        String max = "A";
        for (PhaseStatistics phaseStatistics : phasesStatistics){
            String str = phaseStatistics.getPhasestatistics().getCongestionindex();
            if (str == null) continue;
            if (max.compareToIgnoreCase(str) < 0) {
                max = phaseStatistics.getPhasestatistics().getCongestionindex();
            }
        }
        return max;
    }

    /**
     * 路口占有率
     * @param detectors
     * @return
     */
    private int getIntersectionOccupyrate(List<VolumeDetector> detectors) {
        int totalOccupyrate = 0;
        for (VolumeDetector detector : detectors) {
            totalOccupyrate += detector.getOccupyrate();
        }
        return divide(totalOccupyrate,detectors.size());
    }

    /**
     * 路口流量饱和度
     * @param detectors
     * @param agentid
     * @param volumeMap
     * @return
     */
    private int getIntersectionAvgSaturation(List<VolumeDetector> detectors, String agentid, Map<Integer, Map<String, Integer>> volumeMap) {
        List<Integer> idList = new ArrayList<>();
        for (VolumeDetector detector : detectors) {
            idList.add(detector.getId());
        }

        int saturationValue = getDetectorSaturationValue(idList, agentid, volumeMap);

        return divide(saturationValue,detectors.size());
    }


    /**
     * 路口总流量
     * @param detectors
     * @return
     */
    private int getIntersectionTotalFlow(List<VolumeDetector> detectors) {
        int totalFlow = 0;
        for (VolumeDetector detector : detectors) {
            totalFlow += detector.getSmallvehnum();
        }
        return totalFlow;
    }

    /**
     * 处理信号机上报的检测器状态
     * @param msg
     * @param key
     * @return
     */
    @Override
    public MessageData processDetectorStatus(MessageData msg, String key) {

        String value = mDao.getValueFromRedis(key);
        // redis中没有当前路口的检测器信息，直接更新
        if (value == null)  return msg;

        JsonElement recvData = msg.getData();
        String createTime = msg.getCreatetime();

        // 检测器状态
        StatusDetector statusDetector;
        try {
            statusDetector = gson.fromJson(recvData, StatusDetector.class);
        } catch (JsonSyntaxException e){
            logger.warning("Json parse Exception: " + e.getMessage());
            return msg;
        }

        // 上报的车辆检测器状态
        List<Detector> detectors = statusDetector.getDetector();
        setCreateTime(createTime,detectors);
        // 上报的io版检测器状态
        List<Detector> statusDetectorIo = statusDetector.getIo();
        setCreateTime(createTime,statusDetectorIo);

        msg.setData(gson.toJsonTree(statusDetector));

        // redis中存在当前路口的检测器信息，增量更新
        StatusDetector statusDetectorFromRedis;
        try {
            MessageData dataFromRedis = gson.fromJson(value, MessageData.class);
            statusDetectorFromRedis = gson.fromJson(dataFromRedis.getData(), StatusDetector.class);
        } catch (JsonSyntaxException e){
            logger.warning("Json parse Exception: " + e.getMessage());
            return msg;
        }

        // redis中保存的车辆检测器
        if (!CollectionUtils.isEmpty(detectors)){
            List<Detector> detectorFromRedis = statusDetectorFromRedis.getDetector();
            List<Detector> detectorList = updateDetector(detectorFromRedis, detectors);
            statusDetectorFromRedis.setDetector(detectorList);
        }
        // redis中保存的io版
        if (!CollectionUtils.isEmpty(statusDetectorIo)){
            List<Detector> detectorIoFromRedis = statusDetector.getIo();
            List<Detector> detectorIoFromRedisList = updateDetector(detectorIoFromRedis, statusDetectorIo);
            statusDetectorFromRedis.setIo(detectorIoFromRedisList);
        }

        msg.setData(gson.toJsonTree(statusDetectorFromRedis));

        return msg;
    }

    /**
     * 获取路口历史统计数据
     * @param agentid
     * @param jsonObject
     * @return
     */
    @Override
    public IntersectionStatisticsVo getHistoryIntersectionStatistics(String agentid, JsonObject jsonObject) {

        IntersectionStatisticsVo vo = new IntersectionStatisticsVo();

        String beginTime = jsonObject.get("beginTime").getAsString();
        String endTime = jsonObject.get("endTime").getAsString();

        String dataLabel = "intersectionStatistics";

        List<String> values = mDao.getStatistics(agentid,dataLabel,beginTime,endTime);

        List<IntersectionStatistics> list = new ArrayList<>();
        if (!CollectionUtils.isEmpty(values)) {
            IntersectionStatisticsVo intersectionStatisticsVo;
            for (String value : values) {
                try {
                    intersectionStatisticsVo = gson.fromJson(value, IntersectionStatisticsVo.class);
                } catch (JsonSyntaxException e){
                    logger.warning("Json parse Exception: " + e.getMessage());
                    continue;
                }

                if (intersectionStatisticsVo == null || CollectionUtils.isEmpty(intersectionStatisticsVo.getIntestatistics())) continue;

                List<IntersectionStatistics> intestatistics = intersectionStatisticsVo.getIntestatistics();
                setCreateTime(intersectionStatisticsVo.getCreateTime(),intestatistics);

                list.addAll(intestatistics);
            }
        }
        vo.setIntestatistics(list);
        return vo;
    }

    /**
     * 获取路口实时统计数据
     * @param agentid
     * @return
     */
    @Override
    public IntersectionStatisticsVo getIntersectionStatistics(String agentid) {
        String key = "asc:statistics/intersection:" + agentid;
        String value = mDao.getValueFromRedis(key);

        IntersectionStatisticsVo vo = new IntersectionStatisticsVo();
        vo.setIntestatistics(new ArrayList<>());
        if (value == null) return vo;

        try {
            vo = gson.fromJson(value, IntersectionStatisticsVo.class);
        } catch (JsonSyntaxException e){
            logger.warning("Json parse Exception: " + e.getMessage());
        }

        vo.setCreateTime(null); // 序列化去除时间
        return vo;
    }

    /**
     * 获取路口车道信息
     * @param data
     * @return
     */
    @Override
    public List<Vehile> getIntersectionLaneParam(String data) {
        List<Vehile> vehiles = new ArrayList<>();
        JsonObject vehileJsonObject = gson.fromJson(data, JsonObject.class);
        JsonElement vehileJsonElement = vehileJsonObject.get("vehile");
        if (vehileJsonElement == null) {
            return vehiles;
        }

        List<Vehile> vehile = gson.fromJson(vehileJsonElement, new TypeToken<List<Vehile>>() {
        }.getType());
        vehiles.addAll(vehile);
        return vehiles;
    }

    @Override
    public int getSectionCount(String agentid) {
        String tableName = "t_section";
        if (mDao.isTableExist(tableName)) {
            Map<String,Object> map = mDao.getSectionByAgentId(agentid);
            return ((Long)map.get("count")).intValue();
        }
        return 0;
    }

    @Override
    public List<AscsBaseModel> getDevsBySql(String sql) {
        return mDao.getDevByPara(sql);
    }

    @Override
    public JsonArray getLaneDirection(String agentid) {
        JsonArray directions = new JsonArray();
        MessageData featureAll = commServiceDown.getFeatureAllFromDB(agentid);
        Set<Integer> dirSet = new HashSet<>();
        if (featureAll != null) {
            // 处理相位和跟随相位的方向
            JsonArray phaseElement = featureAll.getData().getAsJsonObject().get("phaseList").getAsJsonArray();
            JsonArray overlapElement = featureAll.getData().getAsJsonObject().get("overlaplList").getAsJsonArray();
            phaseElement.addAll(overlapElement);
            for (JsonElement phase : phaseElement) {
                JsonElement direction = phase.getAsJsonObject().get("direction");
                List<Integer> dirs = gson.fromJson(direction,new TypeToken<List<Integer>>(){}.getType());
                processDirection(dirs,dirSet);
            }
        }
        // 方向拆分为方位和转向
        splitDirection(dirSet,directions);
        return directions;
    }

    /**
     * 相位中的方向拆分为方位和转向
     * @param dirSet
     * @param directions
     */
    private void splitDirection(Set<Integer> dirSet, JsonArray directions) {
        if (!CollectionUtils.isEmpty(dirSet)) {
            for (Integer dir : dirSet) {
                JsonObject jsonObject = new JsonObject();

                Integer lanePosition = parseLanePosition(dir);
                Integer laneDirection = parseLaneDirection(dir);

                jsonObject.addProperty("lanePosition",lanePosition);
                jsonObject.addProperty("laneDirection",laneDirection);

                directions.add(jsonObject);
            }
        }
    }

    /**
     * 解析转向
     * @param dir
     * @return
     */
    private Integer parseLaneDirection(Integer dir) {
        Integer laneDirection = 1;
        switch (dir) {
            case 1:
            case 5:
            case 9:
            case 13:
                laneDirection = 1; // 直行
                break;
            case 2:
            case 6:
            case 10:
            case 14:
                laneDirection = 2; // 左转
                break;
            case 3:
            case 7:
            case 11:
            case 15:
                laneDirection = 3; // 右转
                break;
            case 4:
            case 8:
            case 12:
            case 16:
                laneDirection = 4; // 掉头
                break;
        }
        return laneDirection;
    }

    /**
     * 解析方位
     * @param dir
     * @return
     */
    private Integer parseLanePosition(Integer dir) {
        Integer lanePosition = 1;
        switch (dir) {
            case 1:
            case 2:
            case 3:
            case 4:
                lanePosition = 1; // 东
                break;
            case 5:
            case 6:
            case 7:
            case 8:
                lanePosition = 2; // 西
                break;
            case 9:
            case 10:
            case 11:
            case 12:
                lanePosition = 4; // 北
                break;
            case 13:
            case 14:
            case 15:
            case 16:
                lanePosition = 3; // 南
                break;
        }
        return lanePosition;
    }

    /**
     *
     * @param dirs
     * @param dirSet
     */
    private void processDirection(List<Integer> dirs, Set<Integer> dirSet) {
        if (!CollectionUtils.isEmpty(dirs)) {
            for (Integer dir : dirs) {
                dirSet.add(dir);
            }
        }
    }

    /**
     * 计算相位统计数据
     * @param phaseArray
     * @param detectorArray
     * @param agentid
     * @param volumeMap
     * @return
     */
    private List<PhaseStatistics> computePhasesStatistics(JsonArray phaseArray, JsonArray detectorArray, String agentid, Map<Integer, Map<String, Integer>> volumeMap) {

        List<PhaseStatistics> phaseStatisticsList = new ArrayList<>(phaseArray.size());
        String time = DateUtil.date2str(new Date());
        // 只要有相位，不管有无统计数据，都返回
        for (JsonElement phase : phaseArray) {
            int phaseNo = phase.getAsJsonObject().get("id").getAsInt();

            PhaseStatistics ps = new PhaseStatistics();
            ps.setPhaseno(phaseNo);
            ps.setTime(time);

            PhaseStatisticsData psdata = getPhaseStatisticsData(phaseNo, detectorArray, agentid,volumeMap);

            ps.setPhasestatistics(psdata);
            phaseStatisticsList.add(ps);
        }

        return phaseStatisticsList;
    }


    /**
     *
     * @param phaseNo
     * @param detectorArray
     * @param agentid
     * @param volumeMap
     * @return
     */
    private PhaseStatisticsData getPhaseStatisticsData(int phaseNo, JsonArray detectorArray, String agentid, Map<Integer, Map<String, Integer>> volumeMap) {
        PhaseStatisticsData phaseStatisticsData = new PhaseStatisticsData();
        // 没有上报的交通流数据，返回一个空的相位
        if (volumeMap == null) return phaseStatisticsData;

        // 参与数据计算的检测器编号
        List<Integer> detectorsIdList = getDetectorsIdList(phaseNo,detectorArray);
        if (detectorsIdList.size() == 0)  // 说明当前相位没有配置检测器
            return phaseStatisticsData;

        Map<String,Integer> totalMap = computeDetectorsTotalData(detectorsIdList,volumeMap);

        // 总流量
        int totalFlow = totalMap.get("totalFlow");
        // 平均流量
        int averageFlow = computeAverageFlow(totalFlow,detectorsIdList.size());
        // 相位平均占有率
        int occupyrate = divide(totalMap.get("totalOccupyrate"),detectorsIdList.size());
        // 平均绿灯利用率
        int greenUsage = divide(totalMap.get("totalGreenUsage"),detectorsIdList.size());
        // 流量饱和度(总)
        int saturation = getDetectorSaturationValue(detectorsIdList, agentid,volumeMap);
        int avgSaturation =  divide(saturation,detectorsIdList.size());
        // 相位拥堵指数（综合饱和度和绿灯利用率计算）
        String congestionIndex = computeCongestionIndex(avgSaturation,greenUsage);

        phaseStatisticsData.setTotalflow(totalFlow);
        phaseStatisticsData.setOccupyrate(occupyrate);
        phaseStatisticsData.setAverageflow(averageFlow);
        phaseStatisticsData.setSaturation(avgSaturation);
        phaseStatisticsData.setCongestionindex(congestionIndex);
        phaseStatisticsData.setGreenusage(greenUsage);
        return phaseStatisticsData;
    }

    /**
     * 计算相位平均流量
     * @param totalFlow
     * @param size
     * @return
     */
    private int computeAverageFlow(int totalFlow, int size) {
        return size == 0 ? 0 : totalFlow / size;
    }


    /**
     *
     * @param detectorsIdList
     * @param agentid
     * @param volumeMap
     * @return
     */
    private int getDetectorSaturationValue(List<Integer> detectorsIdList, String agentid, Map<Integer, Map<String, Integer>> volumeMap) {

        int total = 0;
        for (Integer id : detectorsIdList) {

            Map<String, Integer> map = volumeMap.get(id); // 信号机上报的当前检测器的数据
            if (map == null) continue;

            Integer flow = map.get("flow"); // 该检测器的前5分钟统计的流量

            Map<Integer, Integer> detectorDefaultFlow = detectorDataMap.get(agentid); // 检测器默认的流量饱和值

            int defalutValue = 0;
            if (detectorDefaultFlow != null && detectorDefaultFlow.get(id) != null) {
                defalutValue = detectorDefaultFlow.get(id);
            }

            // 当前流量大于默认流量饱和值
            if (flow > defalutValue) {
                defalutValue = getModifyValue(defalutValue,flow);
            }

            total += divide(flow,defalutValue);
        }

        return total;

    }

    /**
     * 更新检测器修正记录
     * @param agentid
     * @param key
     */
    private void updateDetectorModifyStatus(String agentid, Integer key) {
        // 每次修改检测器的默认饱和流量值，都要更新检测器的修改状态
        Map<Integer, Long> map = detectorUpdateRecord.get(agentid);
        if (map == null) {
            map = new ConcurrentHashMap<>();
            detectorUpdateRecord.put(agentid,map);
        }
        map.put(key,System.currentTimeMillis());
    }

    /**
     * 修正默认流量饱和度
     * @param defaultValue
     * @param currentValue
     * @return
     */
    private int getModifyValue(int defaultValue, int currentValue) {
        int modifyValue = 0;
        int addValue = currentValue - defaultValue;
        // 如果增量超过了默认值一半，增量较大，扩充为当前流量的1.5倍
        if (addValue >= defaultValue >> 1) {
            modifyValue = currentValue + (currentValue >> 1);
        }
        // 如果增量少于默认值的一半，增量一般，扩充为当前车流量 + 增量
        else {
            modifyValue = currentValue + addValue;
        }
        return modifyValue;
    }




    /**
     * 计算检测器的总数据
     * @param detectorsIdList
     * @param volumeMap
     * @return
     */
    private Map<String, Integer> computeDetectorsTotalData(List<Integer> detectorsIdList, Map<Integer, Map<String, Integer>> volumeMap) {

        Map<String,Integer> map = new HashMap<>();
        int totalFlow = 0;
        int totalOccupyrate = 0;
        int totalGreenUsage = 0;

        for (Integer id : detectorsIdList) {
            Map<String, Integer> detectorDataMap = volumeMap.get(id);
            if (detectorDataMap == null || detectorDataMap.get("flow") == null)
                continue;
            totalFlow += detectorDataMap.get("flow");
            totalOccupyrate += detectorDataMap.get("occupyrate");
            totalGreenUsage += detectorDataMap.get("greenusage");

        }
        map.put("totalFlow",totalFlow);
        map.put("totalOccupyrate",totalOccupyrate);
        map.put("totalGreenUsage",totalGreenUsage);

        return map;
    }



    /**
     * 获取该相位中的检测器id
     * @param phaseNo
     * @param detectorArray
     * @return
     */
    private List<Integer> getDetectorsIdList(int phaseNo, JsonArray detectorArray) {
        List<Integer> idList = new ArrayList<>();

        for (JsonElement detector : detectorArray) {
            int callphase = detector.getAsJsonObject().get("callphase").getAsInt(); // 检测器所属相位
            if (phaseNo != callphase)
                continue;
            int id = detector.getAsJsonObject().get("id").getAsInt();   // 检测器id
            idList.add(id);
        }
        return idList;
    }


    /**
     * 避免by/zero
     * @param total
     * @param count
     * @return
     */
    private int divide(int total,int count) {
        if (count == 0) return 0;

        if (Math.round((double)total / count) > 1) {
            return (int) Math.round((double)total / count);
        }

        return (int) Math.round((double)total / count * 100);
    }

    /**
     * 综合流量饱和度和占有率计算拥堵指数
     * @param saturation
     * @param occupyrate
     * @return
     */
    private String computeCongestionIndex(int saturation, int occupyrate) {
        String congestionIndex;
//        if (saturation >= 0 && saturation <= 30){
//            if (occupyrate < 80){
//                congestionIndex = "A";
//            } else {
//                congestionIndex = "F";
//            }
//        } else if (saturation > 30 && saturation <= 70){
//            if (occupyrate < 80){
//                congestionIndex = "B";
//            } else {
//                congestionIndex = "E";
//            }
//        } else {
//            if (occupyrate < 80){
//                congestionIndex = "C";
//            } else {
//                congestionIndex = "D";
//            }
//        }
        if (occupyrate < 80) {
            if (saturation >=0 && saturation <= 30) {
                congestionIndex = "A";
            } else if (saturation > 30 && saturation <= 70){
                congestionIndex = "B";
            } else {
                congestionIndex = "C";
            }
        } else {
            if (saturation >=0 && saturation <= 30) {
                congestionIndex = "F";
            } else if (saturation > 30 && saturation <= 70){
                congestionIndex = "E";
            } else {
                congestionIndex = "D";
            }
        }


        return congestionIndex;
    }

    /**
     * 获取有效检测器
     * @param detectors
     * @return
     */
    private List<Detector> getEffectiveDetector(List<Detector> detectors) {
        if (CollectionUtils.isEmpty(detectors)) return new ArrayList<>();

        String pattern = "yyyy-MM-dd HH:mm:ss.SSS";
        int effectiveTime = 15000;  // 毫秒
        List<Detector> effectiveDetectors = new ArrayList<>();
        for (Detector detector : detectors) {
            boolean expire = compareCreateTime(detector.getCreateTime(), pattern, effectiveTime);
            if (expire) continue;
            // 序列化时去除创建时间
            detector.setCreateTime(null);
            effectiveDetectors.add(detector);
        }
        return effectiveDetectors;
    }

    /**
     * 增量更新redis中检测器状态
     * @param detectorFromRedis redis中保存的检测器状态
     * @param detectors 信号机上报的检测器状态
     * @return
     */
    private List<Detector> updateDetector(List<Detector> detectorFromRedis, List<Detector> detectors) {

        Map<Integer,Detector> detectorFromRedisMap;

        if (CollectionUtils.isEmpty(detectorFromRedis)){
            detectorFromRedisMap = new HashMap<>();
        } else {
            detectorFromRedisMap = detectorFromRedis.stream().collect(Collectors.toMap(Detector::getId,detector -> detector));
        }

        for (Detector detector : detectors) {
            detectorFromRedisMap.put(detector.getId(),detector);
        }

        return new ArrayList<>(detectorFromRedisMap.values());
    }


    /**
     * 为集合数据添加时间
     * @param createTime
     * @param
     */
    private void setCreateTime(String createTime,Object data) {
        List list;
        try {
            list = (List) data;
        } catch (ClassCastException e) {
            logger.warning("set time failed! " + e.getMessage());
            return;
        }

        if (CollectionUtils.isEmpty(list)) return;

        Iterator iterator = list.iterator();
        Object obj = list.get(0);
        while (iterator.hasNext()) {
            if (obj instanceof IntersectionStatistics) {
                IntersectionStatistics inte = (IntersectionStatistics) iterator.next();
                inte.setTime(createTime);
            }
            else if (obj instanceof Detector) {
                Detector detector = (Detector) iterator.next();
                detector.setCreateTime(createTime);
            }
        }

    }

}
